﻿#region 참조 네임스페이스

using Skying.Diagnostics.Logger;
using Skying.Drawing;
using Skying.IO;
using Skying.Net.NetworkInformation;
using Skying.Net.Sockets;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

#endregion

namespace MJpegWriterAgent
{
	public partial class MainForm : Form
	{
		#region 변수

		/// <summary>
		/// 로거를 나타낸다.
		/// </summary>
		private Logger _logger = Logger.GetLogger();

		/// <summary>
		/// 클라이언트의 접속을 받아들이는 TCP 리스너를 나타낸다.
		/// </summary>
		private Skying.Net.Sockets.TcpListener _listener;

		/// <summary>
		/// 라우터와의 통신을 담당하는 클라이언트를 나타낸다.
		/// </summary>
		private Skying.Net.Sockets.TcpClient _client;

		/// <summary>
		/// 캡쳐된 이미지 목록 소스를 나타낸다.
		/// </summary>
		private IEnumerable<Image> _imageSource;

		#endregion

		#region 생성자/소멸자

		public MainForm()
		{
			this.InitializeComponent();

			// 전체 화면의 캡쳐 이미지를 연속적으로 이미지 소스로 가져온다.
			this._imageSource = ScreenCapturing.CaptureSequentially(SystemInformation.VirtualScreen);

			this.Load += (s, e) =>
			{
				IPAddress ipAddress;

				if (NetworkInformation.GetActiveIpAddress(out ipAddress))
				{
					this.IpAddressTextBox.Text = ipAddress.ToString();
				}
			};
		}

		#endregion

		#region 리스너

		/// <summary>
		/// 리스너를 시작한다.
		/// </summary>
		private void StartListener()
		{
			try
			{
				int portNumber = (int)this.PortNumericUpDown.Value;

				this._listener = new Skying.Net.Sockets.TcpListener(portNumber, TransferType.Send, false);

				this._listener.ListeningStarted = (eventArgs) =>
				{
					string logString = string.Format("Listening @ {0}:{1}", this._listener.Address, this._listener.Port);

					this.ListenerStripStatusLabel.Text = logString;

					this.StartButton.Enabled = false;
					this.StopButton.Enabled = true;

					this._logger.Debug(logString);
				};

				this._listener.ListeningStopped = (eventArgs) =>
				{
					this.ListenerStripStatusLabel.Text = "Stopped";

					this.StartButton.Enabled = true;
					this.StopButton.Enabled = false;

					this._logger.Debug("Stopped");
				};

				this._listener.AcceptCompleted = (eventArgs) =>
				{
					this.ConnectedClientCountStripStatusLabel.Text = string.Format("{0} connected", this._listener.ClientsCount);

					this._logger.Debug(string.Format("Client {0} conncedted from {1}:{2}", eventArgs.Result.Id, eventArgs.Result.Address, eventArgs.Result.Port));

					ThreadPool.QueueUserWorkItem((userState) =>
					{
						using (MJpegWriter writer = new MJpegWriter(new NetworkStream(eventArgs.Result.InternalSocket, true)))
						{
							if (!this._listener.IsListening) return;

							try
							{
								writer.WriteHeader();

								foreach (MemoryStream stream in this.GetImageStreams())
								{
									Thread.Sleep((int)this.IntervalNumericUpDown.Value);

									writer.Write(stream);
								}
							}
							catch (Exception)
							{
								eventArgs.Result.DisconnectAsynchrous();
							}
						}
					});
				};

				this._listener.ClientDisconnected = (eventArgs) =>
				{
					this.ConnectedClientCountStripStatusLabel.Text = string.Format("{0} connected", this._listener.ClientsCount);

					this._logger.Debug(string.Format("Client {0} disconnected from {1}:{2}", eventArgs.Result.Id, eventArgs.Result.Address, eventArgs.Result.Port));
				};

				this._listener.Error = (eventArgs) =>
				{
					this._logger.Error(eventArgs.Result);
				};

				this._listener.Listen();
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// 리스너를 중지시킨다.
		/// </summary>
		private void StopListener()
		{
			try
			{
				if (!this._listener.IsListening) return;

				this._listener.Close();
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		#endregion

		#region 라우터 클라이언트

		/// <summary>
		/// 라우터로 연결한다.
		/// </summary>
		private void ConnectToRouter()
		{
			try
			{
				IPAddress ipAddress;
				int portNumber;

				if (!IPAddress.TryParse(this.IpAddressTextBox.Text, out ipAddress))
				{
					return;
				}

				portNumber = (int)this.ClientPortNumericUpDown.Value;

				this._client = new Skying.Net.Sockets.TcpClient(TransferType.Send);

				this._client.Connected = (eventArgs) =>
				{
					string logString = string.Empty;

					if (eventArgs.Error == null)
					{
						logString = string.Format("Connected to {0}", this._client.Address, this._client.Port);

						this.ClientStripStatusLabel.Text = logString;

						this.ConnectButton.Enabled = false;
						this.DisconnectButton.Enabled = true;

						ThreadPool.QueueUserWorkItem((userState) =>
						{
							using (MJpegWriter writer = new MJpegWriter(new NetworkStream(this._client.InternalSocket, true)))
							{
								try
								{
									writer.WriteHeader();

									foreach (MemoryStream stream in this.GetImageStreams())
									{
										Thread.Sleep((int)this.IntervalNumericUpDown.Value);

										writer.Write(stream);
									}
								}
								catch (Exception)
								{
									this._client.DisconnectAsynchrous();
								}
							}
						});
					}
					else
					{
						logString = eventArgs.Error.Message;
					}

					this._logger.Debug(logString);
				};
				
				this._client.Disconnected = (eventArgs) =>
				{
					this.ClientStripStatusLabel.Text = "Disconnected";

					this.ConnectButton.Enabled = true;
					this.DisconnectButton.Enabled = false;

					this._logger.Debug("Disconnected");
				};

				this._client.Error = (eventArgs) =>
				{
					this._logger.Error(eventArgs.Error);
				};

				this._client.ConnectAsynchrous(new IPEndPoint(ipAddress, portNumber));
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// 라우터와의 연결을 끊는다.
		/// </summary>
		private void DisconnectFromRouter()
		{
			try
			{
				this._client.DisconnectAsynchrous();
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		#endregion

		#region 캡쳐

		/// <summary>
		/// 캡쳐된 이미지의 스트림을 가져온다.
		/// </summary>
		/// <returns>캡쳐된 이미지 목록에 대한 이미지 스트림이 반환된다.</returns>
		private IEnumerable<MemoryStream> GetImageStreams()
		{
			foreach (Image image in this._imageSource)
			{
				MemoryStream stream = new MemoryStream();

				image.Save(stream, ImageFormat.Jpeg);

				yield return stream;
			}
		}

		#endregion

		#region 버튼 이벤트

		/// <summary>
		/// Start 버튼이 클릭된 경우 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void StartButton_Click(object sender, EventArgs e)
		{
			this.StartListener();
		}

		/// <summary>
		/// Stop 버튼이 클릭된 경우 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void StopButton_Click(object sender, EventArgs e)
		{
			this.StopListener();
		}

		/// <summary>
		/// Connect 버튼이 클릭된 경우 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ConnectButton_Click(object sender, EventArgs e)
		{
			this.ConnectToRouter();
		}

		/// <summary>
		/// Disconnect 버튼이 클릭된 경우 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DisconnectButton_Click(object sender, EventArgs e)
		{
			this.DisconnectFromRouter();
		}

		#endregion	
	}
}